﻿/*
Copyright (c) 2011 Vratislav Cermak (vratacermak@gmail.com)
 
This file is part of TeachMe Project. See http://code.google.com/p/teach-me for more information.
 
TeachMe is an open source project. Using of TeachMe is free of charge.
It is distributed under GNU General Public License version 3 (GPLv3).
GPLv3 can be found at http://www.gnu.org/licenses/gpl.html.
 
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using LumenWorks.Framework.IO.Csv;
using TeachMe.Application.Configuration;
using TeachMe.Application.Data;
using TeachMe.Application.Properties;

namespace TeachMe.Application.Forms
{
    using TeachMe.Application.ComboItems;

    public partial class CsvImportForm : PseudoModalForm
    {
        private enum ColumnIds
        {
            From,
            To,
            Example
        }

        private readonly Root _root;
        private readonly ErrorProviders _errorProviders;

        public List<SimpleEntry> Result { get; set; }

        public ImportContext ImportContext { get; set; }

        public CsvImportForm()
        {
            InitializeComponent();
        }

        public CsvImportForm(Root root)
        {
            InitializeComponent();

            _errorProviders = new ErrorProviders();
            _root = root;
            _root.UserSettings.CheckOut();
            _root.MachineSettings.CheckOut();

            InitializeUserInterface();
            LoadDefaults();
            UpdateControls();

            WindowProportions.Lock(this, _root.MachineSettings);
            GridProportions.Lock(this, grid, _root.MachineSettings);
        }

        private void UpdateControls()
        {
            udFrom.Enabled = cbFrom.Checked;
            udTo.Enabled = cbTo.Checked;
            udExample.Enabled = cbExample.Checked;
        }

        private void InitializeUserInterface()
        {
            foreach (Encoding e in Encodings.Items)
            {
                EncodingInfo ei = Encoding.GetEncodings().FirstOrDefault(y => y.CodePage == e.CodePage);
                if (ei != null)
                {
                    cbEncoding.Items.Add(new ComboBoxItem<EncodingInfo>(ei, ei.DisplayName));
                }
            }

            cbTrimming.Items.Add(new ComboBoxItem<ValueTrimmingOptions>(ValueTrimmingOptions.All, Resources.TrimmingAll));
            cbTrimming.Items.Add(new ComboBoxItem<ValueTrimmingOptions>(ValueTrimmingOptions.None, Resources.TrimmingNone));
            cbTrimming.Items.Add(new ComboBoxItem<ValueTrimmingOptions>(ValueTrimmingOptions.QuotedOnly, Resources.TrimmingQuotedOnly));
            cbTrimming.Items.Add(new ComboBoxItem<ValueTrimmingOptions>(ValueTrimmingOptions.UnquotedOnly, Resources.TrimmingUnquotedOnly));
        }

        private void SaveDefaults()
        {
            CsvImportDefaults defaults = _root.UserSettings.CsvImport;

            defaults.Encoding.Value = ComboBoxItem<EncodingInfo>.GetSelection(cbEncoding).Name;

            if (tbComment.Text.Length > 0)
            {
                defaults.Comment.Value = tbComment.Text[0];
            }
            if (tbDelimeter.Text.Length > 0)
            {
                defaults.Delimeter.Value = tbDelimeter.Text[0];
            }
            if (tbEscape.Text.Length > 0)
            {
                defaults.Escape.Value = tbDelimeter.Text[0];
            }
            if (tbQuote.Text.Length > 0)
            {
                defaults.Quote.Value = tbQuote.Text[0];
            }

            defaults.FromIndex.Value = (short)(cbFrom.Checked ? udFrom.Value : -1);
            defaults.ToIndex.Value = (short)(cbTo.Checked ? udTo.Value : -1);
            defaults.ExampleIndex.Value = (short)(cbExample.Checked ? udExample.Value : -1);

            defaults.Trimming.Value = ComboBoxItem<ValueTrimmingOptions>.GetSelection(cbTrimming);
        }

        private void LoadDefaults()
        {
            CsvImportDefaults defaults = _root.UserSettings.CsvImport;

            cbEncoding.SelectedItem = cbEncoding.Items.Cast<ComboBoxItem<EncodingInfo>>().FirstOrDefault(x => String.Compare(x.Item.Name, defaults.Encoding.Value, true) == 0);

            tbComment.Text = defaults.Comment.Value.ToString();
            tbDelimeter.Text = defaults.Delimeter.Value.ToString();
            tbEscape.Text = defaults.Escape.Value.ToString();
            tbQuote.Text = defaults.Quote.Value.ToString();

            if (defaults.FromIndex.Value > 0)
            {
                udFrom.Value = defaults.FromIndex.Value;
            }
            if (defaults.ToIndex.Value > 0)
            {
                udTo.Value = defaults.ToIndex.Value;
            }
            if (defaults.ExampleIndex.Value > 0)
            {
                udExample.Value = defaults.ExampleIndex.Value;
            }

            cbFrom.Checked = defaults.FromIndex.Value > 0;
            cbTo.Checked = defaults.ToIndex.Value > 0;
            cbExample.Checked = defaults.ExampleIndex.Value > 0;

            cbTrimming.SelectedItem = cbTrimming.Items.Cast<ComboBoxItem<ValueTrimmingOptions>>().FirstOrDefault(x => x.Item == defaults.Trimming.Value);
        }

        private void OnBrowseClick(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.CheckFileExists = true;
            dlg.AutoUpgradeEnabled = true;
            dlg.CheckPathExists = true;
            dlg.Filter = Resources.OpenCsvFilter;
            if (!String.IsNullOrEmpty(_root.MachineSettings.LastBrowseDirectory) && Directory.Exists(_root.MachineSettings.LastBrowseDirectory))
            {
                dlg.InitialDirectory = _root.MachineSettings.LastBrowseDirectory;
            }
            dlg.Multiselect = false;
            dlg.ShowReadOnly = true;
            if (dlg.ShowDialog(this) == DialogResult.OK)
            {
                tbPath.Text = dlg.FileName;

                _root.MachineSettings.LastBrowseDirectory = Path.GetFullPath(dlg.FileName);
            }
        }

        private void OnPreviewClick(object sender, EventArgs e)
        {
            if (!ValidateContent())
            {
                return;
            }

            entriesBindingSource.Clear();

            bool failed;
            List<SimpleEntry> entries = Import(out failed);
            if (!failed)
            {
                foreach (SimpleEntry entry in entries)
                {
                    entriesBindingSource.Add(entry);
                }
            }
        }

        private List<SimpleEntry> Import(out bool failed)
        {
            List<SimpleEntry> data = new List<SimpleEntry>();
            failed = false;

            Dictionary<ColumnIds, int> idxs = new Dictionary<ColumnIds, int>();
            idxs.Add(ColumnIds.From, (int)udFrom.Value - 1);
            idxs.Add(ColumnIds.To, (int)udTo.Value - 1);
            idxs.Add(ColumnIds.Example, (int)udExample.Value - 1);

            try
            {
                using (StreamReader streamReader = new StreamReader(tbPath.Text, Encoding.GetEncoding(ComboBoxItem<EncodingInfo>.GetSelection(cbEncoding).CodePage)))
                {
                    using (CsvReader csvReader = new CsvReader(streamReader, cbFirstRowHasColumnNames.Checked, tbDelimeter.Text[0], tbQuote.Text[0], tbEscape.Text[0], tbComment.Text[0], ComboBoxItem<ValueTrimmingOptions>.GetSelection(cbTrimming)))
                    {
                        try
                        {
                            while (csvReader.ReadNextRecord() && !failed)
                            {
                                SimpleEntry entry = new SimpleEntry();
                                if (cbFrom.Checked)
                                {
                                    if (TestColumnId(idxs[ColumnIds.From], csvReader, ref failed))
                                    {
                                        entry.From = csvReader[idxs[ColumnIds.From]];
                                    }

                                    if (!ImportContext.ImportWordDelegate(ImportContext, entry.From))
                                    {
                                        continue;
                                    }
                                }
                                if (cbTo.Checked)
                                {
                                    if (TestColumnId(idxs[ColumnIds.To], csvReader, ref failed))
                                    {
                                        entry.To = csvReader[idxs[ColumnIds.To]];
                                    }
                                }
                                if (cbExample.Checked)
                                {
                                    if (TestColumnId(idxs[ColumnIds.Example], csvReader, ref failed))
                                    {
                                        entry.Example = csvReader[idxs[ColumnIds.Example]];
                                    }
                                }

                                data.Add(entry);
                            }
                        }
                        catch (Exception exception)
                        {
                            Logging.Error(GetType(), exception, "Error parsing csv file {0}.", tbPath.Text);
                            MessageBox.Show(this, Resources.ErrorWhileParsingCsv, Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);

                            failed = true;
                        }
                    }
                }
            }
            catch (IOException exception)
            {
                Logging.Error(GetType(), exception, "Error parsing csv file {0}.", tbPath.Text);
                MessageBox.Show(this, String.Format(Resources.ErrorReadingFile, tbPath.Text), Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);

                failed = true;
            }

            return data;
        }

        private bool TestColumnId(int index, IDataRecord csvReader, ref bool failed)
        {
            if (index >= csvReader.FieldCount)
            {
                MessageBox.Show(this, String.Format(Resources.InvalidColumnIdxDuringCsvImport, index + 1), Resources.MessageBoxCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
                failed = true;
                return false;
            }

            return true;
        }

        private bool ValidateContent()
        {
            _errorProviders.Clear();

            if (!File.Exists(tbPath.Text))
            {
                _errorProviders.AddError(txPath);
            }

            if (tbDelimeter.Text.Length != 1)
            {
                _errorProviders.AddError(txDelimeter);
            }

            if (tbComment.Text.Length != 1)
            {
                _errorProviders.AddError(txComment);
            }

            if (tbEscape.Text.Length != 1)
            {
                _errorProviders.AddError(txEscape);
            }

            if (tbQuote.Text.Length != 1)
            {
                _errorProviders.AddError(txQuote);
            }

            if (!cbFrom.Checked && !cbTo.Checked && !cbExample.Checked)
            {
                _errorProviders.AddError(cbFrom);
            }

            return _errorProviders.Count == 0;
        }

        private void OnFormClosed(object sender, FormClosedEventArgs e)
        {
            _root.MachineSettings.CheckIn();
        }

        private void OnFormClosing(object sender, FormClosingEventArgs e)
        {
            if (DialogResult == DialogResult.Cancel || DialogResult == DialogResult.None)
            {
                _root.UserSettings.Revert();
                return;
            }

            if (!ValidateContent())
            {
                e.Cancel = true;
                return;
            }

            bool failed;
            Result = Import(out failed);
            if (failed)
            {
                e.Cancel = true;
                return;
            }

            SaveDefaults();

            _root.UserSettings.CheckIn();
        }

        private void OnMappingCheckedChanged(object sender, EventArgs e)
        {
            UpdateControls();
        }
    }
}
